
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Runtime.InteropServices;
using System.Text;

namespace NonIntrusive_Debugger
{
    public class Debugger
    {
        #region DLL Imports
        [DllImport("kernel32.dll")]
        private static extern int CreateProcess(string lpApplicationName, string lpCommandLine, [MarshalAs(UnmanagedType.Struct)] ref  SECURITY_ATTRIBUTES lpProcessAttributes, [MarshalAs(UnmanagedType.Struct)] ref  SECURITY_ATTRIBUTES lpThreadAttributes, int bInheritHandles, int dwCreationFlags, int lpEnvironment, string lpCurrentDriectory, [MarshalAs(UnmanagedType.Struct)] ref  STARTUPINFO lpStartupInfo, [MarshalAs(UnmanagedType.Struct)] ref  PROCESS_INFORMATION lpProcessInformation);
        [DllImport("kernel32.dll")]
        private static extern int  ResumeThread(int  hThread);
        [DllImport("kernel32.dll")]
        private static extern int SuspendThread(int hThread);
        [DllImport("kernel32.dll")]
        private static extern bool GetThreadContext(IntPtr hThread, ref CONTEXT lpContext);
        [DllImport("kernel32.dll")]
        private static extern int SetThreadContext(int hThread, [MarshalAs(UnmanagedType.Struct)] ref  CONTEXT lpContext);
        [DllImport("kernel32.dll")]
        private  static extern int  ReadProcessMemory(int  hProcess,int  lpBaseAddress,byte[]  lpBuffer,int  nSize,ref int  lpNumberOfbytesWritten);
        [DllImport("kernel32.dll")]
        private  static extern int  WriteProcessMemory(int  hProcess,int  lpBaseAddress,byte[]  lpBuffer,int  nSize,ref int lpNumberOfbytesWritten);
        [DllImport("kernel32.dll")]
        private static extern void Sleep(int dwMilliseconds);
        [DllImport("kernel32.dll")]
        private static extern int GetTickCount();
        [DllImport("kernel32.dll")]
        private static extern int GetProcAddress(int hModule, string lpProcName);
        [DllImport("kernel32.dll")]
        private static extern int GetModuleHandle(string lpModuleName);
        [DllImport("kernel32.dll")]
        private static extern int VirtualQueryEx(int hProcess, int lpAddress, [MarshalAs(UnmanagedType.Struct)] ref  MEMORY_BASIC_INFORMATION lpBuffer, int dwLength);
        [DllImport("kernel32.dll")]
        private static extern int TerminateProcess(int hProcess, int uExitCode);
        #endregion
        #region Structs/Enums
        public enum CONTEXT_FLAGS : uint
        {
            CONTEXT_i386 = 0x10000,
            CONTEXT_i486 = 0x10000,   //  same as i386
            CONTEXT_CONTROL = CONTEXT_i386 | 0x01, // SS:SP, CS:IP, FLAGS, BP
            CONTEXT_INTEGER = CONTEXT_i386 | 0x02, // AX, BX, CX, DX, SI, DI
            CONTEXT_SEGMENTS = CONTEXT_i386 | 0x04, // DS, ES, FS, GS
            CONTEXT_FLOATING_POINT = CONTEXT_i386 | 0x08, // 387 state
            CONTEXT_DEBUG_REGISTERS = CONTEXT_i386 | 0x10, // DB 0-3,6,7
            CONTEXT_EXTENDED_REGISTERS = CONTEXT_i386 | 0x20, // cpu specific extensions
            CONTEXT_FULL = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS,
            CONTEXT_ALL = CONTEXT_CONTROL | CONTEXT_INTEGER | CONTEXT_SEGMENTS | CONTEXT_FLOATING_POINT | CONTEXT_DEBUG_REGISTERS | CONTEXT_EXTENDED_REGISTERS
        }
        public enum REGISTERS : int
        {
            EAX, ECX, EDX, EBX, ESP, EBP, ESI, EDI, EIP
        }
        public enum FLAGS : int
        {
            CarryFlag, ParityFlag ,AuxCarryFlag, ZeroFlag, SignFlag, InterruptFlag, DirectionFlag, OverFlowFlag
        }
        public enum SEARCH_STYLES : int
        {
            FindFirst, FindAll
        }
        [StructLayout(LayoutKind.Sequential)]
        public struct SECURITY_ATTRIBUTES
        {
            public int nLength;
            public int lpSecurityDescriptor;
            public int bInheritHandle;
        }
        [StructLayout(LayoutKind.Sequential)]
        public struct STARTUPINFO
        {
            public int cb;
            public string lpReserved;
            public string lpDesktop;
            public string lpTitle;
            public int dwX;
            public int dwY;
            public int dwXSize;
            public int dwYSize;
            public int dwXCountChars;
            public int dwYCountChars;
            public int dwFillAttribute;
            public int dwFlags;
            public int wShowWindow;
            public int cbReserved2;
            public int lpReserved2;
            public int hStdInput;
            public int hStdOutput;
            public int hStdError;
        }
        [StructLayout(LayoutKind.Sequential)]
        public struct PROCESS_INFORMATION
        {
            public int hProcess;
            public int hThread;
            public int dwProcessId;
            public int dwThreadId;
        }
        [StructLayout(LayoutKind.Sequential)]
        public struct FLOATING_SAVE_AREA
        {
            public uint ControlWord;
            public uint StatusWord;
            public uint TagWord;
            public uint ErrorOffset;
            public uint ErrorSelector;
            public uint DataOffset;
            public uint DataSelector;
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 80)]
            public byte[] RegisterArea;
            public uint Cr0NpxState;
        }
        [StructLayout(LayoutKind.Sequential)]
        public struct CONTEXT
        {
            public uint ContextFlags; //set this to an appropriate value 
            // Retrieved by CONTEXT_DEBUG_REGISTERS
            public uint Dr0;
            public uint Dr1;
            public uint Dr2;
            public uint Dr3;
            public uint Dr6;
            public uint Dr7;
            // Retrieved by CONTEXT_FLOATING_POINT
            public FLOATING_SAVE_AREA FloatSave;
            // Retrieved by CONTEXT_SEGMENTS
            public uint SegGs;
            public uint SegFs;
            public uint SegEs;
            public uint SegDs;
            // Retrieved by CONTEXT_INTEGER
            public uint Edi;
            public uint Esi;
            public uint Ebx;
            public uint Edx;
            public uint Ecx;
            public uint Eax;
            // Retrieved by CONTEXT_CONTROL
            public uint Ebp;
            public uint Eip;
            public uint SegCs;
            public uint EFlags;
            public uint Esp;
            public uint SegSs;
            // Retrieved by CONTEXT_EXTENDED_REGISTERS
            [MarshalAs(UnmanagedType.ByValArray, SizeConst = 512)]
            public byte[] ExtendedRegisters;
        }
        [StructLayout(LayoutKind.Sequential)]
        public struct MEMORY_BASIC_INFORMATION
        {
            public int BaseAddress;
            public int AllocationBase;
            public int AllocationProtect;
            public int RegionSize;
            public int State;
            public int Protect;
            public int lType;
        }




        #endregion
        #region Private Variables
        private List <BreakPoint> BreakPoints = new List<BreakPoint>();
        private const int CREATE_SUSPENDED = 0x4;
        private int ProcessID = 0;
        private int ThreadID = 0;
        private int hThread = 0;
        private int hProcess = 0;
        private bool appRunning = false;
        private string searchString = "";
        private bool allowOverwriteBP = false;
        private SEARCH_STYLES searchStyle = SEARCH_STYLES.FindFirst;
        Process appProc = null;
        private InlineASM.InlineASM mlde = new InlineASM.InlineASM("60FC33D28B7424248BEC681CF797106880671CF768189738176818B71C1068172C301768173017186847F515F7684837104C68F7E72C27688760ACF768521C121C681C87107C681C701C20682B6067476811102120682516124068222087826820122047681914101368131027186860828528684540121568C7A01650681218192868121840F268274115196811F0F050B91047121551684712151168121511106815111047B815204712505068101A471280C1105180E9205133C949418BFCAC8AF88A2747C0EC042AC473F68A47FF240F3C0C75035AF7D2423C0074423C0174DB83C7513C0A74D78B7D24423C02742F3C0774353C0B0F8480000000423C03741E3C087424423C047415424260B066F2AE6175024A4A3C09740F2C05746E428BE58B442428891061C3AC8AE0C0E8077212741480C20460B067F2AE61750980EA03FEC875DA424080E40760B067F2AE61741380FC04741780FC0575C3FEC874BF80C204EBBA663D000675B442EBB03C0075ACAC24072C0575A542EBE4F6063875A8B008D0EF1400E970FFFFFF80EFA080FF04738060B067F2AE6175024A4A60B066F2AE610F8474FFFFFF0F8564FFFFFF");

        #endregion

        public Debugger()
        {
            mlde.addProc("getInstrLen", 0);
        }

        public bool Execute(string appToExecute, string commandLine, bool resumeAfterCreate )
        {
            if (appProc != null)
            {
                return false;
            }
            if (appToExecute.ToLower().EndsWith(".exe") == false)
            {
                return false;
            }
            else
            {
                SECURITY_ATTRIBUTES sa1 = new SECURITY_ATTRIBUTES();
                sa1.nLength = Marshal.SizeOf(sa1);
                SECURITY_ATTRIBUTES sa2 = new SECURITY_ATTRIBUTES();
                sa2.nLength = Marshal.SizeOf(sa2);
                STARTUPINFO si = new STARTUPINFO();
                PROCESS_INFORMATION pi = new PROCESS_INFORMATION();

                int ret = CreateProcess(appToExecute, commandLine, ref sa1, ref sa2, 0, CREATE_SUSPENDED, 0, null, ref si, ref pi);
                if (resumeAfterCreate)
                {
                    ret = ResumeThread(pi.hThread);
                    appRunning = true;
                }
                else{ 
                    appRunning = false;
                }

                if (ret != 0)
                {
                    Console.Write("Application Executed!\n");
                    this.ProcessID = pi.dwProcessId;
                    this.ThreadID = pi.dwThreadId;
                    this.hProcess = pi.hProcess;
                    this.hThread = pi.hThread;
                    appProc = Process.GetProcessById(ProcessID);
                    if (appRunning == false)
                    {
                        setBP(getRegister(Debugger.REGISTERS.EAX));
                        waitForBP();
                        clearBP(getRegister(Debugger.REGISTERS.EIP));
                    }
                    
                    return true;
                }
                else 
                {
                    Console.Write("Application Execution Failed!");
                    return false; 
                }
            }
        }

        public void Resume()
        {
            if (appProc == null || appProc.HasExited) { return; }
            if (ProcessID != 0 && appRunning == false)
            {
                ResumeThread(hThread);
                appRunning = true;
            }
        }
        public void Resume(int miliSeconds)
        {
            if (appProc == null || appProc.HasExited) { return; }
            Resume();
            Sleep(miliSeconds);
            Suspend();
        }
        public void Suspend()
        {
            if (appProc == null || appProc.HasExited) { return; }
            if (ProcessID != 0 && appRunning == true)
            {
                SuspendThread(hThread);
                appRunning = false;
            }
        }
        public void Terminate()
        {
            if (appProc == null || appProc.HasExited) { return; }
            if (appRunning == false)
            {
                Resume();
            }
            TerminateProcess(hProcess, 0);
        }


        public int getRegister(REGISTERS regWanted)
        {
            if (appProc == null || appProc.HasExited) { return 0; }
            CONTEXT context = new CONTEXT();
            context.ContextFlags = (uint)CONTEXT_FLAGS.CONTEXT_ALL;
            if (ProcessID != 0)
            {
                if (appRunning)
                {
                    Suspend();
                }
                GetThreadContext(new IntPtr(hThread), ref context);
            }
            switch ((int)regWanted)
            {
                case ((int)REGISTERS.EAX):
                    { return (int)context.Eax; }
                case ((int)REGISTERS.EBP):
                    {return (int)context.Ebp;  }
                case ((int)REGISTERS.EBX):
                    { return (int)context.Ebx; }
                case ((int)REGISTERS.ECX):
                    { return (int)context.Ecx; }
                case ((int)REGISTERS.EDI):
                    { return (int)context.Edi; }
                case ((int)REGISTERS.EDX):
                    { return (int)context.Edx; }
                case ((int)REGISTERS.ESI):
                    { return (int)context.Esi; }
                case ((int)REGISTERS.ESP):
                    { return (int)context.Esp; }
                case ((int)REGISTERS.EIP):
                    { return (int)context.Eip; }
                default:
                    { return 0; }
            }
        }
        public int setRegister(REGISTERS regWanted, int newValue)
        {
            if (appProc == null || appProc.HasExited) { return 0; }
            CONTEXT context = new CONTEXT();
            context.ContextFlags = (uint)CONTEXT_FLAGS.CONTEXT_ALL;
            if (ProcessID != 0)
            {
                if (appRunning)
                {
                    Suspend();
                }
                GetThreadContext(new IntPtr(hThread), ref context);
            }
            switch ((int)regWanted)
            {
                case ((int)REGISTERS.EAX):
                    { context.Eax = (uint)newValue; break; }
                case ((int)REGISTERS.EBP):
                    { context.Ebp = (uint)newValue; break;}
                case ((int)REGISTERS.EBX):
                    { context.Ebx = (uint)newValue; break;}
                case ((int)REGISTERS.ECX):
                    { context.Ecx = (uint)newValue; break;}
                case ((int)REGISTERS.EDI):
                    { context.Edi = (uint)newValue; break;}
                case ((int)REGISTERS.EDX):
                    { context.Edx = (uint)newValue; break;}
                case ((int)REGISTERS.ESI):
                    { context.Esi = (uint)newValue; break;}
                case ((int)REGISTERS.ESP):
                    { context.Esp = (uint)newValue; break;}
                case ((int)REGISTERS.EIP):
                    { context.Eip = (uint)newValue; break; }
            }
            return SetThreadContext(hThread, ref context);
        }

        public bool setBP(int address)
        {
            if (appProc == null || appProc.HasExited) { return false; }
            byte[] oldBytes = new byte[2];
            byte[] bpBytes = new byte[2];
  

            bpBytes[0] = 0xEB;
            bpBytes[1] = 0xFE;

            oldBytes = getData(address, 2);
            if (oldBytes.Length != 2)
            {
                return false;
            }
            BreakPoint newBP = new BreakPoint();
            newBP.setAddress((uint)address);
            newBP.setOldBytes(oldBytes);
            if (setData(address, bpBytes))
            {
                BreakPoints.Add(newBP);
            }

            return true;
        }
        public bool clearBP(int address)
        {
            if (appProc == null || appProc.HasExited) { return false; }
            BreakPoint bpWanted = new BreakPoint();
            
            foreach (BreakPoint x in BreakPoints)
            {
                if (x.getAddress() == address)
                {
                    bpWanted = x;
                }
            }
            if (bpWanted.getAddress() == 0)
            {
                return false;
            }
            else
            {
                int ourAddr = (int)bpWanted.getAddress();
                byte[] oldBytes = new byte[2];
                int numWritten = 0;
                Array.Copy(bpWanted.getOldBytes(), oldBytes, 2);
                if (appRunning)
                {
                    Suspend();
                }
                WriteProcessMemory(hProcess, ourAddr, oldBytes, 2, ref numWritten);
                if (numWritten == 0)
                {
                    return false;
                }
                else
                {
                    BreakPoints.Remove(bpWanted);
                    return true;
                }
            }
        }
        public int waitForBP()
        {
            return waitForBP(1000);
        }
        public bool processHasEnded() { return appProc.HasExited; }
        public int waitForBP(int msToWait)
        {
            if (appProc == null || appProc.HasExited) { return -1; }
            int StartTime = GetTickCount();

            if (appRunning)
            {
                Suspend();
            }
            while (1 == 1)
            {
                int curTime = GetTickCount();
                int eip = getRegister(REGISTERS.EIP);
                foreach (BreakPoint x in BreakPoints)
                {
                    if (x.getAddress() == eip)
                    {
                        //we hit our BP
                        return (int)x.getAddress();
                    }
                    else
                    {
                         curTime = GetTickCount();
                        if (curTime - StartTime > msToWait)
                        {
                            return -1;
                        }
                    }
                }
                curTime = GetTickCount();
                if (curTime - StartTime > msToWait)
                {
                    return -1;
                }
                Resume(10);
                Suspend();
            }

        }
        public bool resetBP(int address)
        {
            if (appProc == null || appProc.HasExited) { return false; }
            return resetBP(address, false);
        }
        public bool resetBP(int address, bool onAPI)
        {
            if (appProc == null || appProc.HasExited) { return false; }
            BreakPoint bpWanted = new BreakPoint();

            foreach (BreakPoint x in BreakPoints)
            {
                if (x.getAddress() == address)
                {
                    bpWanted = x;
                }
            }
            if (bpWanted.getAddress() == 0)
            {
                return false;
            }


            if (onAPI == true)
            {
                int bpAddress = (int)bpWanted.getAddress();
                clearBP(bpAddress);

                setBP(getDWORDValue(getRegister(REGISTERS.ESP)));
                waitForBP(1000);

                clearBP(getRegister(REGISTERS.EIP));

                return setBP(bpAddress);

            }
            else
            {
                int bpAddress = (int)bpWanted.getAddress();
                clearBP(bpAddress);

                Resume(10);

                return setBP(bpAddress);

            }

        }
        public int getFunctionAddress(string function,string module)
        {
            return GetProcAddress(GetModuleHandle(module), function);
        }

        public byte[] getData(int address, int numToRead)
        {
            if (appProc == null || appProc.HasExited) { return new byte[0]; }
            int numRead = 0;
            byte[] theData = new byte[numToRead];
            ReadProcessMemory(hProcess, address, theData, numToRead, ref numRead);
            return theData;
        }
        public int getDWORDValue(int address)
        {
            if (appProc == null || appProc.HasExited) { return 0; }
            byte[] tempData = getData(address, 4);

            return System.BitConverter.ToInt32(tempData, 0);
        }  
        public string getASCII(int address, int maxLength)
        {
            if (appProc == null || appProc.HasExited) { return ""; }
            byte[] tempData = getData(address, maxLength);
            string theString = System.Text.ASCIIEncoding.ASCII.GetString(tempData);
            return theString.Substring(0,theString.IndexOf("\0"));

        }
        
        public int getInstrLen(byte[] opcodes)
        {
            byte[] retVal = new byte[4];
            mlde.runProc("getInstrLen", opcodes, retVal, 0, 0);

            return System.BitConverter.ToInt32(retVal,0);
        }

        public byte[] getOpcode(int address)
        {
            byte[] maxOpcode = new byte[16];
            maxOpcode = getData(address, 16);
            int instrLen = getInstrLen(maxOpcode);
            byte[] trueOpcode = new byte[instrLen];
            Array.Copy(maxOpcode, trueOpcode, instrLen);
            return trueOpcode;
        }

        public byte[] getCurrentOpcode()
        {
            return getOpcode(getRegister(REGISTERS.EIP));
        }

        public byte[] getNextOpcode()
        {
            byte[] curOp = getCurrentOpcode();
            return getOpcode(getRegister(REGISTERS.EIP) + curOp.Length);
        }
        public bool setData(int address, byte[] theData)
        {
            if (appProc == null || appProc.HasExited) { return false; }
            List<int> replacedBPs = new List<int>();
            int bpAddress = 0;
            startAgain:
            foreach (BreakPoint x in BreakPoints)
            {
                if (address <= x.getAddress() && address + theData.Length > x.getAddress())
                {
                    if (allowOverwriteBP == false)
                    {
                        return false;
                    }
                    else
                    {
                        bpAddress = (int)x.getAddress();
                        clearBP(bpAddress);
                        replacedBPs.Add(bpAddress);
                        goto startAgain;
                    }
                }
            }
            int numWritten = 0;
            WriteProcessMemory(hProcess, address, theData, theData.Length, ref numWritten);
            if (replacedBPs.Count > 0)
            {
                foreach (int x in replacedBPs)
                {
                    setBP(x);
                }
            }
            return (numWritten == theData.Length);
        }
        public void setDWORDValue(int address, int newValue)
        {
            if (appProc == null || appProc.HasExited) { return; }
            byte[] tempData = System.BitConverter.GetBytes(newValue);
            setData(address, tempData);
        }
        public bool InjectASM(int address, string ASM)
        {
            if (appProc == null || appProc.HasExited) { return false; }
            if (verifyChars(ASM, false) == false)
            {
                return false;
            }
            byte[] theData = parseHexStringToArray(ASM);

            setData(address, theData);
            return true;
        }
        public bool setASCII(int address, string theString)
        {
            if (appProc == null || appProc.HasExited) { return false; }
            System.Text.ASCIIEncoding encoding = new System.Text.ASCIIEncoding();
            byte[] theBytes = encoding.GetBytes(theString);
            return setData(address, theBytes);
        }

        public int[] searchImage()
        {
            if (appProc == null || appProc.HasExited) { return new int[0]; }
            Process ourProc = Process.GetProcessById(ProcessID);
            ProcessModule ourMod = ourProc.MainModule;
            int searchStart = (int)ourMod.BaseAddress;
            int searchEnd = searchStart + ourMod.ModuleMemorySize;
            if (searchString.Equals("") | verifyChars(searchString, true) == false)
            {
                return new int[1]{-1};
            }
            if (searchString.Length % 2 == 1)
            {
                return new int[1] { -1 };
            }
            byte[] searchBytes = parseHexStringToArray(searchString);
            byte[] byteMask = createByteMask(searchString);

            if (searchBytes.Length == 0 || byteMask.Length == 0)
            {
                return new int[1] { -1 };
                
            }

            //return searchMemory( searchBytes, byteMask,searchStart, searchEnd);
            return searchMem(searchBytes, byteMask, searchStart, searchEnd);
        }
        public int[] searchMemory(int searchStart, int searchEnd)
        {
            if (appProc == null || appProc.HasExited) { return new int[0]; }
            if (searchString.Equals("") | verifyChars(searchString, true) == false)
            {
                return new int[1] { -1 };
            }
            if (searchString.Length % 2 == 1)
            {
                return new int[1] { -1 };
            }
            byte[] searchBytes = parseHexStringToArray(searchString);
            byte[] byteMask = createByteMask(searchString);

            if (searchBytes.Length == 0 || byteMask.Length == 0)
            {
                return new int[1] { -1 };

            }

            //return searchMemory( searchBytes, byteMask,searchStart, searchEnd);
            return searchMem(searchBytes, byteMask, searchStart, searchEnd);
        }
        public int[] searchMemory()
        {
            if (appProc == null || appProc.HasExited) { return new int[0]; }
            //Console.WriteLine("Start: " + GetTickCount().ToString());
            if (searchString.Equals("") | verifyChars(searchString, true) == false)
            {
                return new int[1] { -1 };
            }
            if (searchString.Length % 2 == 1)
            {
                return new int[1] { -1 };
            }
            byte[] searchBytes = parseHexStringToArray(searchString);
            byte[] byteMask = createByteMask(searchString);

            if (searchBytes.Length == 0 || byteMask.Length == 0)
            {
                return new int[1] { -1 };

            }

            //return searchMemory( searchBytes, byteMask,searchStart, searchEnd);
            //Console.WriteLine("End: " + GetTickCount().ToString());
            return searchMem(searchBytes, byteMask, 0, 0x7ffff000);
        }

        public bool setSearchString(string newString)
        {
            if (!verifyChars(newString, true))
            {
                return false;
            }
            searchString = newString;
            return true;
        }
        public void setAllowOverwriteBP(bool newVal)
        {
            allowOverwriteBP = newVal;
        }
        public void setSearchStyle(SEARCH_STYLES newStyle)
        {
            searchStyle = newStyle;
        }

        #region Private Routines
        #endregion
        private bool verifyChars(string theString, bool wildCards)
        {
            string allowedChars = "";
            if (wildCards == false)
            {
                allowedChars =  "0123456789ABCDEFabcdef";
            }
            else
            {
                allowedChars =  "0123456789ABCDEFabcdef?";
            }
            foreach (char x in theString)
            {
                if (allowedChars.IndexOf(x) == -1)
                {
                    return false;
                }
            }
            return true;
        }
        private byte[] parseHexStringToArray(string theString)
        {
            if (theString.Length % 2 == 1)
            {
                return new byte[0];
            }
            byte[] retVal = new byte[theString.Length / 2];
            int offset = 0;
            int count = 0;
            string nextByte = "";
            foreach (byte x in retVal)
            {
                nextByte = theString.Substring(offset, 2);
                if ((nextByte.IndexOf("?") == 0) ^ (nextByte.IndexOf("?", 1) == 1))
                {
                    return new byte[0];
                }
                if (!nextByte.Equals("??"))
                {
                    retVal[count] = byte.Parse(nextByte, System.Globalization.NumberStyles.HexNumber);
                }
                offset += 2;
                count++;
            }
            return retVal;
        }
        private byte[] createByteMask(string theString)
        {
            if (theString.Length % 2 == 1)
            {
                return new byte[0];
            }
            byte[] retVal = new byte[theString.Length / 2];
            int offset = 0;
            int count = 0;
            string nextByte = "";
            foreach (byte x in retVal)
            {
                nextByte = theString.Substring(offset, 2);
                if ((nextByte.IndexOf("?") >= 0) ^ (nextByte.IndexOf("?", 1) >= 0))
                {
                    return new byte[0];
                }
                if (nextByte.Equals("??"))
                {
                    retVal[count] = 1;
                }
                offset += 2;
                count++;
            }
            return retVal;
        }
        private int[] searchMem(byte[] searchBytes, byte[] byteMask, int startAddr, int endAddr)
        {
            if (appProc == null || appProc.HasExited) { return new int[0]; }
            endAddr = (int)((uint)endAddr & (uint)0xFFFFF000);
            MEMORY_BASIC_INFORMATION mbi = new MEMORY_BASIC_INFORMATION();
            int currentAddr = startAddr;
            int blockSize = 0;
            List<int> hits = new List<int>();

            while (1==1)
            {
            nextBlock:
                if (currentAddr >= endAddr )
                {
                    break;
                }
                int ret = VirtualQueryEx(hProcess, (int)currentAddr, ref mbi,Marshal.SizeOf(mbi) );

                blockSize = mbi.RegionSize;
                if (mbi.State == 0x2000 || mbi.State == 0x10000)
                {
                    currentAddr += mbi.RegionSize;
                    goto nextBlock;
                }
                else
                {
                    byte[] currentBlock = new byte[mbi.RegionSize];
                    currentBlock = getData(currentAddr, mbi.RegionSize);
                    //search teh block
                    for (int x = 0; x < (currentBlock.Length - searchBytes.Length); x++)
                    {
                        if (currentBlock[x] == searchBytes[0] || byteMask[0] == 1)
                        {
                            //found match for first byte
                            for (int y = 1; y < searchBytes.Length; y++)
                            {
                                if (currentBlock[x + y] != searchBytes[y] && byteMask[y] != 1)
                                {
                                    break;
                                    
                                }
                                if (y + 1 == searchBytes.Length)
                                {
                                    // found a hit!
                                    hits.Add(currentAddr + x);
                                    if (searchStyle == SEARCH_STYLES.FindFirst)
                                    {
                                        return hits.ToArray();
                                    }
                                }
                            }
                        }
                    }

                    currentAddr += mbi.RegionSize;
                }
            }
            return hits.ToArray();

        }

    }
}
